<?php

namespace iflow\swoole\Sandbox;

use iflow\App;
use iflow\Container\Container;
use iflow\initializer\Config;
use iflow\swoole\implement\Tools\Pool\Coroutine\Context;

class SandboxApplication extends Context {

    protected App $baseApplication;

    protected array $snapshots = [];

    public function initializer(App $app): SandboxApplication {
        $this -> baseApplication = $app;
        return $this;
    }

    public static function getRootCoroutineId(int $cid = 0): int {
        $cid = $cid ?: self::getCoroutineId();
        while (!self::getData('__is_root', cid: $cid)) {
            $cid = self::getPCid($cid);
            if ($cid < 1) break;
        }
        return $cid;
    }

    public function setApp(bool $isRoot = false): App {
        if ($isRoot) {
            self::setData('__is_root', true, self::getCoroutineId());
        }
        return (new Application()) -> runApp($this);
    }

    public function run(callable $callable): void {
        $app = $this -> getApplication();
        try {
            $app -> invokeFunction($callable);
        } catch (\Throwable $exception) {
            $app -> error -> appHandler($exception);
        } finally {
            $this -> clearApp();
        }
    }

    protected function getApplication(): App {
        $app = $this -> snapshots[self::getRootCoroutineId()] ?? null;
        if ($app === null) {
            $app = $this -> setApp(true);
        }
        return $this -> snapshots[self::getRootCoroutineId()] = $app;
    }

    public function getBaseApplication(): App {
        return $this -> baseApplication;
    }

    public function clearApp(?int $cid = 0): void {
        $rootId = $cid ?: self::getRootCoroutineId();
        self::clear($rootId); // TODO: Change the autogenerated stub
        unset($this -> snapshots[$rootId]);
        Container::setInstance($this -> getBaseApplication());
    }

}